// ---------------------------------------------------------------------------------------
package world {
	
	// FLASH IMPORTS
	import flash.display.MovieClip;
	import flash.display.DisplayObject;
	import flash.geom.Point;
	import flash.events.Event;
	import flash.utils.getTimer;
	
	// GAME IMPORTS
	import iso.Iso;
	import items.ItemDef;
	import items.SortableItem;
	import includes.INC;
	
	
	// -----------------------------------------------------------------------------------
	public class World extends MovieClip {

		// reference to parent(stage)
		private var _PARENT:IsoZSort;

		// game state
		private var _uiGameState:uint;
		
		// iso instance
		private var _ISO:Iso;
		
		// arrays
		private var _aItemDefs:Array;
		private var _aSortableItems:Array;
		private var _aAvatars:Array;

		// world vector
		private var _vecWorld:Vector.<int>;
		
		// movieclips
		private var _mcItems:MovieClip;
		private var _mcStats:MovieClip;
		
		// avatar item id
		private var _uiAvatarID:uint;
		
		// timers
		private var _iGameTimer:int;
		private var _iMoveAvatar:int;
		private var _iMoveItem:int;
		private var _iClearText:int;
				
		
		// -------------------------------------------------------------------------------
		// CONSTRUCTOR
		// -------------------------------------------------------------------------------
		public function World( parent:IsoZSort ) {
			
			_PARENT = parent;
			
			_uiGameState = INC.STATE_INIT;
			
			addEventListener( Event.ENTER_FRAME, main );
		}
		
		// -------------------------------------------------------------------------------
		// MAIN GAME LOOP
		// -------------------------------------------------------------------------------
		private function main( e:Event ):void {
		
			_iGameTimer = getTimer();
			
			switch ( _uiGameState ) {
				
				case INC.STATE_INIT:
				
					initVars();
					_uiGameState = INC.STATE_CREATE;
					break;
					
				case INC.STATE_CREATE:
				
					createWorld( _iGameTimer );
					_uiGameState = INC.STATE_IDLE;
					break;
					
				case INC.STATE_IDLE:
				
					idle( _iGameTimer );
					break;
			}
		}
		
		// -------------------------------------------------------------------------------
		private function initVars():void {
			
			// init iso instance
			_ISO = new Iso( INC.TILE_SIZE );

			// init world array - we're using vector and bit shifting for better performance
			_vecWorld = new Vector.<int>( (INC.BIT_WORLD * INC.BIT_WORLD), true );
			for ( var row:uint = 0; row < INC.BIT_WORLD; row++ ) {
				for ( var col:uint = 0; col < INC.BIT_WORLD; col++ ) {
					_vecWorld[ (row << INC.BIT_SHIFT) | col ] = -1;
				}
			}

			// player object id
			_uiAvatarID = 0;
			
			// create item defs - id, width, height
			_aItemDefs = new Array();
			_aItemDefs.push( new ItemDef(  0, 1, 1 ) ); // avatar
			_aItemDefs.push( new ItemDef(  1, 1, 1 ) ); // crate
			_aItemDefs.push( new ItemDef(  2, 2, 1 ) ); // cabinet
			_aItemDefs.push( new ItemDef(  3, 1, 2 ) ); // bench
			_aItemDefs.push( new ItemDef(  4, 3, 1 ) ); // hay
			_aItemDefs.push( new ItemDef(  5, 1, 3 ) ); // hay
			_aItemDefs.push( new ItemDef(  6, 2, 2 ) ); // bush
			_aItemDefs.push( new ItemDef(  7, 6, 1 ) ); // fence
			_aItemDefs.push( new ItemDef(  8, 1, 6 ) ); // fence
			_aItemDefs.push( new ItemDef(  9, 4, 4 ) ); // wheatfield
			_aItemDefs.push( new ItemDef( 10, 2, 3 ) ); // table
			_aItemDefs.push( new ItemDef( 11, 3, 2 ) ); // table
			_aItemDefs.push( new ItemDef( 12, 3, 3 ) ); // petbed

			// item arrays
			_aSortableItems = new Array();
			_aAvatars = new Array();
			
			// movieclips
			_mcItems = new MovieClip();
			_mcStats = new mcStats();
			
			// timers for moving items
			_iMoveAvatar = 0;
			_iMoveItem   = 0;
			
			// timer for text display
			_iClearText = -1;
		}
		
		// -------------------------------------------------------------------------------
		// CREATE ISO WORLD
		// -------------------------------------------------------------------------------
		private function createWorld( t:int ):void {
			
			// show stats mc
			_PARENT.addChild( _mcStats );
			
			// create the iso ground
			_ISO.drawIsoGround( this, INC.WORLD_ROWS, INC.WORLD_COLS );
			_mcStats.tfDraw.text = "drawn iso ground: " + String( getTimer() - t ) + "ms";
			
			// 1000 attempts to place an item
			t = getTimer();
			for ( var i:uint = 0; i < 1000; i++ ) addItem();
			_mcStats.tfAdd.text = "added " + String( _aSortableItems.length ) + " items: " + String( getTimer() - t ) + "ms";

			// show item
			addChild( _mcItems );

			// do the initial sort for all static items
			sortItems();
		}
		
		// -------------------------------------------------------------------------------
		// IDLE FUNC
		// -------------------------------------------------------------------------------
		private function idle( t:int ):void {
			
			// add 100 avatars first
			if ( _aAvatars.length < 100 ) {
				
				addAvatar();
			}
			// time to move a random avatar?
			else if ( _iGameTimer > _iMoveAvatar ) {
				
				moveRandomAvatar();
				_iMoveAvatar = t + 1000;
			}
			// time to move a random item?
			else if ( _iGameTimer > _iMoveItem ) {

				moveRandomItem();
				_iMoveItem = t + 5000;
			}
			
			// time to remove sort text?
			if ( ( _iClearText != -1 ) && ( t > _iClearText ) ) {
				_mcStats.tfSort.text = "";
				_iClearText = -1;
			}
		}
		
		// -------------------------------------------------------------------------------
		// ADD AVATAR
		// -------------------------------------------------------------------------------
		private function addAvatar():void {
		
			var ptPos:Point = getRandomPos( 1, 1 );
			if ( ptPos != null ) {
				
				_vecWorld[ (ptPos.y << INC.BIT_SHIFT) | ptPos.x ] = _uiAvatarID;
				
				var mc:MovieClip = new mcAvatar();
				
				var ptIsoPos:Point = _ISO.getIsoPos( ptPos.x, ptPos.y );
				
				mc.x = ptIsoPos.x;
				mc.y = ptIsoPos.y;
				_mcItems.addChild( mc );
				
				var objSortableItem:SortableItem = new SortableItem( _uiAvatarID, ptPos.y, ptPos.x, 1, 1, mc );
				
				_aAvatars.push( objSortableItem );
				
				// find correct position in display list
				insertItem( objSortableItem );
				
				// update child indices - no resorting required for 1x1 avatar
				setItemIndices();
			}
		}
		
		// -------------------------------------------------------------------------------
		// ITEM FUNCS
		// -------------------------------------------------------------------------------
		private function addItem( itemID:int = -1 ):Boolean {
		
			var bAdded:Boolean = false;
			
			var item:int = itemID;
			if ( item == -1 ) item = 1 +  Math.floor( (_aItemDefs.length - 1) * Math.random() );
			
			var objItemDef:ItemDef = _aItemDefs[ item ];
			
			var ptPos:Point = getRandomPos( objItemDef.uiWidth, objItemDef.uiHeight );
			if ( ptPos != null ) {
			
				var mcItem:MovieClip = new mcItems();
				mcItem.gotoAndStop( objItemDef.uiID );
				mcItem.cacheAsBitmap = true;
				
				var ptIsoPos:Point = _ISO.getIsoPos( ptPos.x, ptPos.y );
				
				mcItem.x = ptIsoPos.x;
				mcItem.y = ptIsoPos.y;
				_mcItems.addChild( mcItem );
				
				var objSortableItem:SortableItem = new SortableItem( objItemDef.uiID,
																	 ptPos.y, ptPos.x,
													                 objItemDef.uiWidth,
													                 objItemDef.uiHeight,
													                 mcItem );
													
				_aSortableItems.push( objSortableItem );
				
				setItem( objSortableItem, objItemDef.uiID );
				
				bAdded = true;
			}
			
			return ( bAdded );
		}
		
		// -------------------------------------------------------------------------------
		private function getRandomPos( width:uint, height:uint ):Point {
			
			var t:int = getTimer();
			
			var ptPos:Point = null;

			// start at a random location
			var row:uint   = Math.floor( INC.WORLD_ROWS * Math.random() );
			var col:uint   = Math.floor( INC.WORLD_COLS * Math.random() );
			
			var startIndex:uint = (row * INC.WORLD_COLS) + col;
			var nextIndex:uint  = startIndex + 1;
			
			var isPlaceable:Boolean = false;
			
			// go around the whole board once to find an availabe spot
			while ( !isPlaceable && (nextIndex != startIndex) ) {
				
				isPlaceable = isItemPlaceable( row, col, width, height );
				
				if ( isPlaceable ) {
					ptPos = new Point( col, row );
				} else {
					if ( ++nextIndex >= (INC.WORLD_ROWS * INC.WORLD_COLS) ) nextIndex = 0;
					row = int(nextIndex / INC.WORLD_COLS);
					col = nextIndex - (row * INC.WORLD_COLS);
				}
			}
			
			return ( ptPos );
		}
		
		// -------------------------------------------------------------------------------
		private function isItemPlaceable( itemRow:uint, itemCol:uint, w:uint, h:uint ):Boolean {
			
			// item placement out of bounds?
			if      ( (itemCol + w) > INC.WORLD_COLS) return ( false );
			else if ( (itemRow + h) > INC.WORLD_ROWS) return ( false );
			
			var bPlacable:Boolean = true;
			
			for ( var row:uint = itemRow; row < (itemRow + h); row++ ) {
				for ( var col:uint = itemCol; col < (itemCol + w); col++ ) {
					
					if ( _vecWorld[ (row << INC.BIT_SHIFT) | col ] != -1 ) {
						bPlacable = false;
						break;
					}
				}
			}
			
			return ( bPlacable );
		}
		
		// -------------------------------------------------------------------------------
		private function setItem( objItem:SortableItem, value:* ):void {
			
			for ( var row:uint = objItem.uiRow; row < (objItem.uiRow + objItem.uiHeight); row++ ) {
				for ( var col:uint = objItem.uiCol; col < (objItem.uiCol + objItem.uiWidth); col++ ) {
					
					_vecWorld[ (row << INC.BIT_SHIFT) | col ] = value;
				}
			}
		}
		
		// -------------------------------------------------------------------------------
		// IDLE FUNCS
		// -------------------------------------------------------------------------------
		private function moveRandomAvatar():void {

			var iRand:int = Math.floor( _aAvatars.length * Math.random() );
			var objItem:SortableItem = _aAvatars[ iRand ];
			
			// remove sortable item
			removeItem( objItem.mc );
			
			// remove avatar reference
			_aAvatars.splice( iRand, 1 );
			
			// add a new one
			addAvatar();
		}
		
		// -------------------------------------------------------------------------------
		private function moveRandomItem():void {

			var iRand:int = Math.floor( _aSortableItems.length * Math.random() );
			var objItem:SortableItem = _aSortableItems[ iRand ];
			
			// make sure it's not an avatar
			while ( objItem.uiID == _uiAvatarID ) {
				iRand = Math.floor( _aSortableItems.length * Math.random() );
				objItem = _aSortableItems[ iRand ];
			}
			
			var uiItemID:uint = objItem.uiID;
			
			// remove sortable item
			removeItem( objItem.mc );

			// resort all items if item was moved/added
			if ( addItem( uiItemID ) ) sortItems();
		}
		
		// -------------------------------------------------------------------------------
		// SORTING
		// -------------------------------------------------------------------------------
		private function sortItems():void {
			
			var t:int = getTimer();
			
			// sort all items by their cell index - front to back for faster inserting!
			_aSortableItems.sort( sortByPos );
			
			// create temp array and reset existing items array
			var aSorted:Array = _aSortableItems.slice(0);
			_aSortableItems = [];
			
			var objNew:SortableItem;
			
			// insert items from temp array
			for ( var i:uint = 0; i < aSorted.length; ++i ) {
								
				objNew = aSorted[i];
				
				insertItem( objNew );
			}
			
			// set child index for items
			setItemIndices();
			
			// show sort text for 2 seconds
			_mcStats.tfSort.text = "sort " + _aSortableItems.length + " items: " + String( getTimer() - t ) + "ms";
			_iClearText = getTimer() + 2000;
		}
		
		// -------------------------------------------------------------------------------
		// THIS IS THE ACTUAL Z-SORT LOGIC
		// -------------------------------------------------------------------------------
		private function insertItem( objNewItem:SortableItem ):void {
			
			var objItem:SortableItem;
			var bAdded:Boolean = false;
			
			for ( var i:uint = 0; i < _aSortableItems.length; i++ ) {
				
				objItem = _aSortableItems[i];
				
				// is newItem behind item[i]?
				if ( (objNewItem.uiCol <= objItem.uiEndCol) && (objNewItem.uiRow <= objItem.uiEndRow) ) {
					
					_aSortableItems.splice( i, 0, objNewItem );
					bAdded = true;
					break;
				}
			}
			
			if ( !bAdded ) _aSortableItems.push( objNewItem );
		}
		
		// -------------------------------------------------------------------------------
		// DELETE AN ITEM FROM THE LIST
		// -------------------------------------------------------------------------------
		private function removeItem( mc:MovieClip ):void {

			var objSortableItem:SortableItem;
			
			for ( var i:uint = 0; i < _aSortableItems.length; ++i ) {
				
				objSortableItem = _aSortableItems[i];
				
				if ( objSortableItem.mc == mc ) {
					
					_mcItems.removeChild( objSortableItem.mc );	// remove mc
					_aSortableItems.splice( i, 1 ); 			// remove sortable item from list
					setItem( objSortableItem, -1 );				// reset item grid cells
					objSortableItem = null;						// free object reference

					break;
				}
			}
		}
		
		// -------------------------------------------------------------------------------
		// SET CHILDINDEX FOR ALL SORTABLE ITEMS
		// -------------------------------------------------------------------------------
		private function setItemIndices():void {
			
			var t:int = getTimer();
			
			var objSortableItem:SortableItem;
			
			for ( var i:uint = 0; i < _aSortableItems.length; i++ ) {
				
				objSortableItem = _aSortableItems[i];
				
				// only set childIndex if it's different from current childIndex
				if ( _mcItems.getChildIndex( _aSortableItems[i].mc ) != i ) {
					_mcItems.setChildIndex( DisplayObject( _aSortableItems[i].mc ), i ); 
				}
			}
			
			_mcStats.tfIndex.text = "setChildIndex: " + String( getTimer() - t ) + "ms";			
		}
		
		// -------------------------------------------------------------------------------
		// SORT ITEMS BY THEIR CELL INDEX - FRONT TO BACK
		// -------------------------------------------------------------------------------
		private function sortByPos( a:SortableItem, b:SortableItem ):int {
			
			if ( (a.uiRow * INC.WORLD_COLS + a.uiCol) < (b.uiRow * INC.WORLD_COLS + b.uiCol) ) {
				return( 1 );
			}
			else return( -1 );
		}
	}
}